閱讀本篇文章前,仔細想想看
- 前端的 Debug 技巧有哪些?
- 編譯過後的檔案通常會有對應的 Source Map 檔,其中 Source Map 到底是在做什麼呢?
如果還沒理解完畢的話,可以先翻看前一篇文章喔!
筆者知道這邊一直討論編譯器設定有些人覺得很無聊。
但想要開開心心學 TypeScript,有時候一些潛藏的功能理解了就會事半功倍。
今天筆者就要來介紹更多可以加入的語法檢測選項,讀者可能會疑惑:“恩... TypeScript 不是光靠型別推論與註記就飽了嗎?還有附帶語法檢測?”
對,這邊就比較屬於常聽到的 Linter 層面設定的東西 —— 如果開發一些專案應該有都會有相對應的 Style Guide,尤其筆者常用的是 AirBnB 廠商出的 JavaScript Style Guide;而 Linter 最主要的目的是實踐 Style Guide 上面的規則應用到專案裡 —— 提升程式碼的品質(Code Quality)。
所以記得,不是型別系統提供的功能,今天要講的是偏向於 Linter 層面的 TypeScript 編譯器設定。當然還會稍微討論一些跟 Style Guide 有關的東西喔!
[2019.10.14 14:41 新增訊息]
筆者最近的身體修養狀況開始逐漸不錯(
筆者參加鐵人賽其實是在養病的時候參加的 XD),可能一個禮拜之後會開始繼續找工作。然而筆者希望可以維護文章品質 —— 完整把系列推播完畢,鐵人 30 天後的延伸系列ㄧ樣會儘量以一天一篇文的方式推播,但如果真的也沒辦法趕上一天一文,會以兩天一文的方式推播,不過這應該也是幾個禮拜以後的狀況。
事實上,官方並沒有推出正式的 Style Guide,請參見這帖 StackOverflow。
不過仔細想想背後的原因,在 StackOverflow 上面的帖子講得很清楚:
The TypeScript team doesn't issue an "official" style guide for other projects using TypeScript. The guidelines for working on the compiler itself are both too specific and not broad enough for general use;(... 略)
Any JavaScript style guide that is up-to-date for ES6 is going to cover nearly all TypeScript constructs except for type annotations,(... 略)
TSLint is a good choice for enforcing style rules around types / type annotations.
筆者簡單說明:TypeScript 的本質就只是在原本的 JavaScript 包裝一層型別系統與更多延伸語法(Class、Decorators 等等),所以普通的 JavaScript Linter 大部分就可以 Cover 掉所有的語法檢測的各種狀況。
另外,TypeScript 唯一可能需要被語法檢測的部分也就只有型別系統的註記語法(Type Annotation)有沒有符合規定而已 —— 所以才會出現 TSLint 這個東西。
這裡必須要額外注意的點是:TSLint 目前有被 Deprecate 的可能,因為要直接跟 ESLint 進行結合。(如圖一)
圖一:官方的 TSLint 宣告,在 2019 年的某個時間點將會把 TSLint 結合到 ESLint
因此如果你看到這個 Issue 的訊息,裡面就是在講述 TSLint 結合到 ESLint 的過程。(如圖二)
圖二:正式的 TSLint -> ESLint 的 Proposal
有興趣的讀者可以自行看看。所以也有可能讀者看到本篇時,TSLint 早就被 ESLint 結合了也說不定喔!
因此本篇正式進入跟語法檢測有關的功能,第一個就是要講一開始筆者常用的 strickNullChecks
—— 這東西也被筆者拖得夠久了,算是系列文章的債務?
strickNullChecks
設定什麼叫做將 Nullable Type 看待為原始型別?
難道從一開始,Day 02. 提到的原始型別筆者講錯了嗎?(難道筆者錯了嗎!?)
事實上,筆者在《前線維護》篇章系列時,強調過筆者習慣將 strictNullChecks
啟用,因為這會使得 null
與 undefined
自成一個型別 —— 因此這裡筆者就得反過來討論:strickNullChecks
為 false
的狀態會是什麼。
以下是簡單的範例程式碼。
讀者沒看錯,就是這麼蠢的範例。
讀者如果仔細讀過本系列原始型別篇章,遇到遲滯性指派(Delayed Initialization)的案例時,變數 a
還在未確定之前就被使用 —— 一定會觸發 TDZ 錯誤(Temporal Dead Zone)。
因此編輯器內部的結果會出現 TypeScript 的錯誤訊息。(如圖三)
圖三:變數 a
還未被指派正確的值前,就被使用了
所以如果你強行用 tsc
進行編譯會出現錯誤訊息的提醒。(如圖四)
圖四:錯誤訊息跟編輯器內部狀況一模ㄧ樣
但是如果將 strickNullChecks
設定為 false
,編譯結果會是成功的。(可以使用 tsc --strickNullChecks false
來編譯檔案)(結果如圖五)
圖五:使用 --strickNullChecks false
的結果是,會忽略變數本身為 Nullable Types 的狀態
讀者發現了嗎?
如果將
strickNullChecks
設定為false
,則會讓 Nullable Types 視為任何型別可以出現的一種形式
反過來說,如果 strickNullChecks
是 true
,則 Nullable Types 會被強行變成一種型別看待,不會隱身在各種型別裡。也因此,Nullable Types 唯有幾個可以被指派到的地方除了是 Nullable Types 以外,就是any
型別。
貼心小提示
這是筆者遇到的狀況 —— 就算沒在
tsconfig.json
啟用strictNullChecks
,以筆者當前的環境來說 —— 預設值似乎是true
,但官方文件寫的是false
。(圖六為官方文件的截圖;圖七為沒有動tsconfig.json
的結果;圖八為strickNullChecks
刻意被改成false
的結果)但筆者懷疑的情況有兩種:
- 筆者的環境很不乾淨(
如果是這樣,筆者面對讀者會很尷尬,連自己環境都搞不好)- TypeScript 官方沒更新
不過筆者傾向於可能是情形 1,筆者自己的環境問題,因為其他的選項都有按照
tsconfig.json
預設的規則來進行。不過這一篇要展示筆者親身測試過的篇章,因此才會稍微繞了幾圈,請讀者見諒。所以筆者還是要跟讀者講說 —— 記得好好把需要的編譯器選項都開啟;有開啟的話,也就不需要管說會不會有潛藏的雷點。
圖六:strictNullChecks
在官方預設是 false
,可是筆者在自己的環境每次測的時候都是預設為 true
圖七:明明官方說 strickNullChecks
預設值是 false
,但是卻還是有 strickNullChecks
的 Feature
圖八:刻意將 strictNullChecks
關掉,警告訊息就消失了
所以呢~如果讀者還是看不懂或不知道 strictNullChecks
是怎麼一回事,你可以想成:將 strickNullChecks
啟用的話,null
或 undefined
會被視為獨立的原始型別,而本系列就是採取 strictNullChecks
為 true
的前提下進行解說的!
重點 1. Nullable Types 為獨立原始型別 ——
strictNullChecks
strictNullChecks
模式下,會將 Nullable Types 視為獨立的原始型別,因此:
- 任何被註記為 Nullable Types 的變數只能接收該 Nullable Types
- Nullable Types 的值除了能夠被指派到有註記到 Nullable Types 的變數外,也可以被指派到
any
型別的變數
any
型態 —— noImplicitAny
讀者這下子應該知道《前線維護》篇章的重要性了,還記得 Implicit Any 是指什麼案例嗎?XD
貼心小提示
如果真的忘記的話,趕快看看函式型別篇章補一補!
不過這也代表讀者對型別推論與註記方面的了解還不夠扎實喔,建議把《前線維護》篇章系列好好看過喔!
以下是測試 noImplicitAny
機制的範例程式碼。(也很短沒錯)
其中,Implicit Any 的情境下 —— 意旨在宣告的函式,裡面的參數若沒有進行積極註記,就會被 TypeScript 推論為 any
型別,但也因此使得 TypeScript 沒辦法追蹤函式到最後的輸出型別。因此會出現圖九的警告訊息。
圖九:Implicit Any 會被 TypeScript 主動警告
貼心小提示(第二彈)
事實上,這邊筆者測試時覺得怪 —— 明明官方 Doc 也是說
noImplicitAny
預設值是false
,但筆者怎麼測預設值都是true
的狀態,看剛剛的圖九就知道了。不過筆者還是先歸咎有可能是自己的環境問題導致,所以還是告誡讀者:每一次建立 TypeScript 專案時,記得要好好檢查自己的編譯器
tsconfig.json
設定是不是正確的喔!
(以下圖十為官方 Doc 截圖;圖十ㄧ為 noImplicitAny
設定為 false
的狀態;圖十二為 noImplicitAny
設定為 true
的狀態)
圖十:官方 Doc 明確將 noImplicitAny
的預設值設定為 false
圖十一:這是 noImplicitAny
為 false
的狀態,TS 照樣會關照你,但不會強制叫你改
圖十二:筆者在自己環境的測試結果 noImplicitAny
為 true
的狀態跟平常沒兩樣
重點 2. 隱性
any
型別狀態的預防 ——noImplicitAny
若將
tsconfig.json
裡的noImplicitAny
啟動時,任何函式的宣告裡 —— 參數若被 TypeScript 判定為any
型別時,就會警告開發者有潛在的錯誤 —— 在這裡也就是所謂的 Implicit Any 的案例。
strict*
系列筆者認為讀者真的需要這些檢測再去查詢用法 —— 因為筆者認為目前的型別檢測設定事實上是足夠應付普通專案的。
以下就列出相關的型別檢測設定:
strictFunctionTypes
strictBindCallApply
strictPropertyInitialization
no*
系列同第 3 點所述,筆者依然認為目前的設定足夠,不過讀者會想使用這裡的設定的機率應該會大過於第三點裡的設定。
以下就列出相關的語法檢測設定:
noUnusedLocals
—— 如果有變數沒有被使用就會出現警告noUnusedParameters
—— 如果函式裡的參數沒有被使用,就會發出警告noImplicitReturns
—— 如果函式裡有出現路徑是沒有回傳值就會發出警告noFallthroughCasesInSwitch
—— 每個 switch
裡面的 case
判斷敘述式一定要有 break
語法,不能有 case
沒有 break
以至於會執行到下一個 case
noImplicitThis
—— this
如果被 TypeScript 判定為 any
型態時就會發出警告這些就是筆者會斟酌使用在專案裡面的設定。
筆者也認為這些設定並不太需要認真解說,讀者也可以試試看這些設定的效果。
筆者到這裡可以正式宣布對 TypeScript 編譯器設定的解說告一段落。
有些筆者可能會問:“恩... 不是還有實驗性功能相關設定嗎?”
哦~這個嘛~
要等筆者寫到《進化實驗》篇章,也就是第五篇章才會揭曉。(E04,到底要寫到什麼時候?XDDD)
下一篇,筆者要回歸講解 TypeScript 更多的功能囉~!敬請期待~